package com.young.lib_fragments.utils

import androidx.annotation.IdRes
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
import androidx.lifecycle.Lifecycle
import com.young.lib_fragments.anim.DefaultFmTransactionAnim
import com.young.lib_fragments.anim.IFmTransactionAnim
import com.young.lib_fragments.fms.BaseFragment
import com.young.lib_fragments.fms.interfaces.IFmShowCompleted
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

/**
 * @author huangzehui
 * @date 2025/1/10 13:11 周五
 * @description Fragment 的转场操作工具类
 */
object FmTransitionsUtil {

    // fragment 展示完成回调延时时间，单位：ms
    const val FM_SHOW_COMPLETED_DELAY_TIME = 300L

    /**
     * 添加一个 fragment
     *
     * @param fmManager Fragment 管理器
     * @param containerViewId fragment 容器 id
     * @param fm 需要添加的 fragment
     * @param fmTag fragment 的 tag，可通过 fmManager.findFragmentByTag() 获取到 fragment
     * @param isToBackStack 是否将 fragment 添加到回退栈中
     * @param toBackStackName 在回退栈中的名称，可通过 fmManager.popBackStack 等类似方法，回退指定 fragment
     * @param fmTransactionAnim 转场动画
     *
     */
    @JvmOverloads
    @JvmStatic
    fun addFm(
        fmManager: FragmentManager,
        @IdRes
        containerViewId: Int,
        fm: Fragment,
        fmTag: String? = null,
        isToBackStack: Boolean = true,
        toBackStackName: String? = null,
        fmTransactionAnim: IFmTransactionAnim = DefaultFmTransactionAnim()
    ) {
        addFm(
            fmManager = fmManager,
            fmTransaction = fmManager.beginTransaction(),
            containerViewId = containerViewId,
            fm = fm,
            fmTag = fmTag,
            isToBackStack = isToBackStack,
            toBackStackName = toBackStackName,
            fmTransactionAnim = fmTransactionAnim
        )
    }

    /**
     * 添加一个 fragment
     *
     * @param fmManager Fragment 管理器
     * @param fmTransaction Fragment 事务管理器
     * @param containerViewId fragment 容器 id
     * @param fm 需要添加的 fragment
     * @param fmTag fragment 的 tag，可通过 fmManager.findFragmentByTag() 获取到 fragment
     * @param isToBackStack 是否将 fragment 添加到回退栈中
     * @param toBackStackName 在回退栈中的名称，可通过 fmManager.popBackStack 等类似方法，回退指定 fragment
     * @param fmTransactionAnim 转场动画
     *
     */
    @JvmOverloads
    @JvmStatic
    fun addFm(
        fmManager: FragmentManager,
        fmTransaction: FragmentTransaction,
        @IdRes
        containerViewId: Int,
        fm: Fragment,
        fmTag: String? = null,
        isToBackStack: Boolean = true,
        toBackStackName: String? = null,
        fmTransactionAnim: IFmTransactionAnim = DefaultFmTransactionAnim()
    ) {
        // 动画一定要放在具体操作(add)之前，否则无效
        fmTransaction.setCustomAnimations(
            fmTransactionAnim.getEnterAnimResId(),
            fmTransactionAnim.getExitAnimResId(),
            fmTransactionAnim.getPopEnterAnimResId(),
            fmTransactionAnim.getPopExitAnimResId()
        )

        if (containerViewId == 0) {
            fmTransaction.add(fm, fmTag)
        } else {
            fmTransaction.add(containerViewId, fm, fmTag)
        }


        if (isToBackStack) {
            fmTransaction.addToBackStack(toBackStackName)
            HandleExceptionBackStackUtil.bindFragmentToBackStackEntry(fmManager, fm)
        }
        fmTransaction.commit()
    }

    /**
     * 添加Fragment，同时移除上一个 fragment
     *
     * @param fmManager Fragment 管理器
     * @param containerViewId fragment 容器 id
     * @param fm 需要添加的 fragment
     * @param lastFm 上一个 Fragment
     * @param fmTag fragment 的 tag，可通过 fmManager.findFragmentByTag() 获取到 fragment
     * @param isToBackStack 是否将 fragment 添加到回退栈中
     * @param toBackStackName 在回退栈中的名称，可通过 fmManager.popBackStack 等类似方法，回退指定 fragment
     * @param fmTransactionAnim 转场动画
     * @param showCompletedTime 展示完成回调的延时时间
     */
    @JvmOverloads
    @JvmStatic
    fun addWithPopFm(
        fmManager: FragmentManager,
        @IdRes
        containerViewId: Int,
        fm: Fragment,
        lastFm: Fragment,
        fmTag: String? = null,
        isToBackStack: Boolean = true,
        toBackStackName: String? = null,
        fmTransactionAnim: IFmTransactionAnim = DefaultFmTransactionAnim(),
        showCompletedTime: Long = FM_SHOW_COMPLETED_DELAY_TIME
    ) {

        if (fm is BaseFragment) {
            fm.fmShowCompleted = object : IFmShowCompleted {
                override fun onShowCompleted() {
                    removeFragment(fmManager, lastFm)
                }

                override fun getDelayMillis(): Long {
                    return showCompletedTime
                }
            }
        } else {
            GlobalScope.launch(Dispatchers.IO) {
                delay(showCompletedTime)
                try {
                    removeFragment(fmManager, lastFm)
                } catch (_: Exception) {
                }
            }
        }

        val fmTransaction = fmManager.beginTransaction()

        // 动画一定要放在具体操作(add)之前，否则无效
        fmTransaction.setCustomAnimations(
            fmTransactionAnim.getEnterAnimResId(),
            fmTransactionAnim.getExitAnimResId(),
            fmTransactionAnim.getPopEnterAnimResId(),
            fmTransactionAnim.getPopExitAnimResId()
        )

        if (containerViewId == 0) {
            fmTransaction.add(fm, fmTag)
        } else {
            fmTransaction.add(containerViewId, fm, fmTag)
        }


        if (isToBackStack) {
            fmTransaction.addToBackStack(toBackStackName)
            HandleExceptionBackStackUtil.bindFragmentToBackStackEntry(fmManager, fm)
        }

        fmTransaction.commit()
    }

    /**
     * 返回上一个页面
     *
     * @param fmManager Fragment 管理器
     */
    @JvmStatic
    fun pop(fmManager: FragmentManager) {
        fmManager.popBackStack()
    }

    /**
     * 返回到根页面
     *
     * @param fmManager Fragment 管理器
     */
    @JvmStatic
    fun pop2Root(fmManager: FragmentManager) {
        fmManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
    }

    /**
     * 回退到 toBackStackName 对应的栈的 fragment
     *
     * @param fmManager Fragment 管理器
     * @param toBackStackName 回退栈的名称，就是 addToBackStack 时的名称
     * @param isInclude true: fmTag 对应的fragment也回退 false：保留 fmTag 对应的 fragment 不回退
     */
    @JvmStatic
    fun pop2BackStackNameFm(fmManager: FragmentManager, toBackStackName: String, isInclude: Boolean) {
        if (isInclude) {
            fmManager.popBackStack(toBackStackName, FragmentManager.POP_BACK_STACK_INCLUSIVE)
        } else {
            fmManager.popBackStack(toBackStackName, 0)
        }
    }


    /**
     * 回退到 tag 对应的栈的 fragment
     *
     * @param fmManager Fragment 管理器
     * @param id 回退栈的id，可以理解为角标，从 0 开始
     * @param isInclude true: id 对应的fragment也回退 false：保留 id 对应的 fragment 不回退
     */
    @JvmStatic
    fun pop2IdFm(fmManager: FragmentManager, id: Int, isInclude: Boolean) {
        if (isInclude) {
            fmManager.popBackStack(id, FragmentManager.POP_BACK_STACK_INCLUSIVE)
        } else {
            fmManager.popBackStack(id, 0)
        }
    }

    /**
     * 移除到指定的 Fragment Cls 对应的 Fragment
     *
     * @param fmManager Fragment 管理器
     * @param fragmentCls Fragment 对应的 cls
     * @param isInclude 是否将 fragmentCls 的fragment也移除，true：是 false保留 fragmentCls 的fragment
     *
     */
    @JvmStatic
    fun pop2TargetFmCls(
        fmManager: FragmentManager,
        fragmentCls: Class<out Fragment>,
        isInclude: Boolean
    ) {
        var fragments = fmManager.fragments
        if (fragments.isEmpty()) {
            return
        }

        // 反向，让最新的fragment在最上面
        fragments = fragments.reversed()

        // 最顶部的 fragment
        val topFm = fragments.first()


        // 目标 fragment 的个数
        var targetFmCount = fragments.filter {
            it::class.java == fragmentCls
        }.size

        if (targetFmCount == 0) {
            pop(fmManager)
            return
        }

        if (targetFmCount == 1 && fragmentCls == topFm::class.java) {
            // 如果目标的fragment在集合中只有一个，而且是顶部的fragment，则返回上一个即可
            pop(fmManager)
            return
        }

        // 是否首次找到目标 Fragment
        var isFirstFindTargetFm = true

        val beginTransaction = fmManager.beginTransaction()

        // 是否除非了移除操作
        var isRemoved = false

        for (fm in fragments) {
            if (topFm == fm) {
                // 顶部的 fragment 先排除，使用后面的 Pop 去移出栈，这样可以保留动画
                continue
            }
            if (fm::class.java == fragmentCls) {
                if (isInclude) {
                    isRemoved = true
                    beginTransaction.remove(fm)
                } else {
                    if (isFirstFindTargetFm) {
                        isFirstFindTargetFm = false
                    } else {
                        isRemoved = true
                        beginTransaction.remove(fm)
                    }
                }
                --targetFmCount
                if (targetFmCount <= 0) {
                    break
                }
            } else {
                isRemoved = true
                beginTransaction.remove(fm)
            }
        }

        beginTransaction.commit()

        if (isRemoved) {
            pop(fmManager)
        }
    }


    /**
     * 移除 Fragment
     *
     * @param fmManager Fragment 管理器
     * @param fm 需要删除的 fragment
     */
    @JvmStatic
    fun removeFragment(fmManager: FragmentManager, fm: Fragment?) {
        if (fm == null) {
            return
        }

        try {
            fmManager.beginTransaction()
                .remove(fm)
                .commit()
        } catch (_: Exception) {
            fmManager.beginTransaction()
                .remove(fm)
                .commitAllowingStateLoss()
        }

    }


    /**
     * 展示 Fragment
     *
     * @param fmManager Fragment 管理器
     * @param containerViewId Fragment 的容器id
     * @param fm 需要展示的 fragment
     * @param fmTag Fragment 的 tag
     * @param fmTransactionAnim 转场动画
     *
     */
    @JvmStatic
    fun show(
        fmManager: FragmentManager,
        @IdRes
        containerViewId: Int,
        fm: Fragment,
        fmTag: String? = null,
        fmTransactionAnim: IFmTransactionAnim = DefaultFmTransactionAnim()
    ) {
        val fmTransaction = fmManager.beginTransaction()

        // 动画一定要放在具体操作(add)之前，否则无效
        fmTransaction.setCustomAnimations(
            fmTransactionAnim.getEnterAnimResId(),
            fmTransactionAnim.getExitAnimResId(),
            fmTransactionAnim.getPopEnterAnimResId(),
            fmTransactionAnim.getPopExitAnimResId()
        )

        if (fmManager.fragments.contains(fm)) {
            fmTransaction.show(fm)
        } else {
            fmTransaction.add(containerViewId, fm, fmTag)
        }

        fmTransaction.commit()
    }


    /**
     * 隐藏 Fragment
     *
     * @param fmManager Fragment 管理器
     * @param fm 需要展示的 fragment
     * @param fmTransactionAnim 转场动画
     *
     */
    @JvmStatic
    fun hide(
        fmManager: FragmentManager,
        fm: Fragment,
        fmTransactionAnim: IFmTransactionAnim = DefaultFmTransactionAnim()
    ) {
        val fmTransaction = fmManager.beginTransaction()

        // 动画一定要放在具体操作(add)之前，否则无效
        fmTransaction.setCustomAnimations(
            fmTransactionAnim.getEnterAnimResId(),
            fmTransactionAnim.getExitAnimResId(),
            fmTransactionAnim.getPopEnterAnimResId(),
            fmTransactionAnim.getPopExitAnimResId()
        )

        fmTransaction.hide(fm)

        fmTransaction.commit()
    }


    /**
     * 展示 Fragment
     *
     * @param fmManager Fragment 管理器
     * @param containerViewId Fragment 的容器id
     * @param showFm 需要展示的 fragment
     * @param showFmTag Fragment 的 tag
     * @param hideFm 需要隐藏的 Fragment
     * @param fmTransactionAnim 转场动画
     */
    @JvmStatic
    fun showAndHide(
        fmManager: FragmentManager,
        @IdRes
        containerViewId: Int,
        showFm: Fragment,
        showFmTag: String? = null,
        hideFm: Fragment?,
        fmTransactionAnim: IFmTransactionAnim = DefaultFmTransactionAnim()
    ) {
        val fmTransaction = fmManager.beginTransaction()

        // 动画一定要放在具体操作(add)之前，否则无效
        fmTransaction.setCustomAnimations(
            fmTransactionAnim.getEnterAnimResId(),
            fmTransactionAnim.getExitAnimResId(),
            fmTransactionAnim.getPopEnterAnimResId(),
            fmTransactionAnim.getPopExitAnimResId()
        )

        hideFm?.let {
            fmTransaction.hide(it)
            // 这里使用新的方式，设置懒加载，上一个页面设置为最大生命周期Lifecycle.State.STARTED，则会走 onPause
            fmTransaction.setMaxLifecycle(it, Lifecycle.State.STARTED)
        }

        if (fmManager.fragments.contains(showFm)) {
            fmTransaction.show(showFm)
        } else {
            fmTransaction.add(containerViewId, showFm, showFmTag)
        }

        // 这里使用新的方式，设置懒加载，当前页面设置为最大生命周期Lifecycle.State.RESUMED，则会走 onResume
        fmTransaction.setMaxLifecycle(showFm, Lifecycle.State.RESUMED)



        fmTransaction.commit()
    }

}